﻿/********************************************************************************
* Copyright 2010 Zane Thorn (zane.thorn@gmail.com)                              *
*                                                                               *
* NeturalMath is free software: you can redistribute it and/or modify           *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation, either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* NeturalMath is distributed in the hope that it will be useful,                *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with NeturalMath.  If not, see <http://www.gnu.org/licenses/>.          *
********************************************************************************/

using System;
using NeturalMath.Language;

namespace MathConsole
{
    public class ConsoleEditor:ConsoleInterface
    {
        #region Local Members

        private const ConsoleColor VARIABLE_COLOR = ConsoleColor.DarkBlue;
        private const ConsoleColor FUNCTION_COLOR = ConsoleColor.Magenta;
        private const ConsoleColor DOMAIN_COLOR = ConsoleColor.DarkCyan;
        private const ConsoleColor OPERATOR_COLOR = ConsoleColor.Black;
        private const ConsoleColor COMMENT_COLOR = ConsoleColor.Green;
        private const ConsoleColor KEYWORD_COLOR = ConsoleColor.Blue;
        private const ConsoleColor ERROR_COLOR = ConsoleColor.Red;
        private const ConsoleColor STRING_COLOR = ConsoleColor.DarkRed;
        private const ConsoleColor CONSTANT_COLOR = ConsoleColor.DarkGray;

        //private static readonly string VARIABLE_EXPRESSION = Lexer.VARIABLE_USE;
        //private static readonly string FUNCTION_EXPRESSION = Lexer.FUNCTION_CALL;
        //private static readonly string DOMAIN_EXPRESSION = "(" + Lexer.DOMAIN_BEGIN + ")|(" + Lexer.DOMAIN_END + ")|("+Lexer.VARIABLE_USE +@"[\.])";
        //private static readonly string OPERATOR_EXPRESSION = @"(\+)|(\-)|(\\)|(\*)|(\^)|(\%)|(\=)|(\=\=)|(\|)|(\&)|(\`)|(\~)|(\!)|(\()|(\))";
        //private static readonly string COMMENT_EXPRESSION = Lexer.COMMENT;
        //private static readonly string KEYWORD_EXPRESSION = FormatDef(Lexer.IMPORT_KEYWORD) +
        //                                                    FormatDef(Lexer.PRINT_KEYWORD) +
        //                                                    FormatDef(Lexer.def_KEYWORD) +
        //                                                    FormatDef(Lexer.TRUE_KEYWORD) +
        //                                                    FormatDef(Lexer.FALSE_KEYWORD) +
        //                                                    FormatDef(Lexer.VOID_KEYWORD).Replace("|",string.Empty);

        //private static readonly string STRING_EXPRESSION = Lexer.STRING_CONSTANT;
        //private static readonly string CONSTANT_EXPRESSION = Lexer.NUMERIC_CONSTANT;

        //private static readonly Regex VARIABLE_MATCH = new Regex(VARIABLE_EXPRESSION);
        //private static readonly Regex FUNCTION_MATCH = new Regex(FUNCTION_EXPRESSION);
        //private static readonly Regex DOMAIN_MATCH = new Regex(DOMAIN_EXPRESSION);
        //private static readonly Regex OPERATOR_MATCH = new Regex(OPERATOR_EXPRESSION);
        //private static readonly Regex COMMENT_MATCH = new Regex(COMMENT_EXPRESSION);
        //private static readonly Regex KEYWORD_MATCH = new Regex(KEYWORD_EXPRESSION);
        //private static readonly Regex STRING_MATCH = new Regex(STRING_EXPRESSION);
        //private static readonly Regex CONSTANT_MATCH = new Regex(CONSTANT_EXPRESSION);

        //private static readonly Regex ALL_MATCHES;

        #endregion

        #region Constructors

        //static ConsoleEditor()
        //{
        //    var builder = new StringBuilder();
        //    builder.Append(FormatDef(COMMENT_EXPRESSION));
        //    builder.Append(FormatDef(STRING_EXPRESSION));
        //    builder.Append(FormatDef(CONSTANT_EXPRESSION));
        //    builder.Append(FormatDef(KEYWORD_EXPRESSION));
        //    builder.Append(FormatDef(DOMAIN_EXPRESSION));
        //    builder.Append(FormatDef(FUNCTION_EXPRESSION));
        //    builder.Append(FormatDef(OPERATOR_EXPRESSION));
        //    builder.Append(FormatDef(VARIABLE_EXPRESSION));
        //    builder.Append(FormatDef(@"\s"));

        //    builder.Append(@"(\.*)");

        //    ALL_MATCHES = new Regex(builder.ToString());
        //}

        public ConsoleEditor()
        {
            
        }

        #endregion

        #region Public Properties

        public int LineNumber { get; private set; }
        public int RowPosition { get; private set; }
        

        public string CurrentLine
        {
            get { return Buffer[LineNumber]; }
            private set { Buffer[LineNumber] = value; }
        }

        public int LineLength
        {
            get { return CurrentLine.Length; }
        }

        public int LineCount
        {
            get { return Buffer.Count; }
        }

        #endregion

        #region Public Methods

        public override bool Read()
        {
            var key = Console.ReadKey(true);
            if (key.Key == ConsoleKey.Escape)
                return DoExit();

            if (key.Modifiers.HasFlag(ConsoleModifiers.Control))
            {

            }
            
            switch (key.Key)
            {
                case ConsoleKey.UpArrow:
                    LineUp();
                    break;
                case ConsoleKey.DownArrow:
                    LineDown();
                    break;
                case ConsoleKey.RightArrow:
                    MoveRight();
                    break;
                case ConsoleKey.LeftArrow:
                    MoveLeft();
                    break;
                case ConsoleKey.Enter:
                    InsertLine();
                    break;
                case ConsoleKey.Escape:
                    return false;
                case ConsoleKey.Backspace:
                    Delete();
                    break;
                default:
                    WriteKey(key.KeyChar);
                    break;
            }
            return true;
        }

        

        public void LineUp()
        {
            if (LineNumber > 0)
            {
                RedrawLine();
                LineNumber--;
                Console.CursorTop--;
            }
        }

        public void LineDown()
        {
            if (LineNumber < LineCount-1)
            {
                RedrawLine();
                LineNumber++;
                Console.CursorTop++;
            }
        }

        public void MoveRight()
        {
            RowPosition++;
            if (RowPosition > CurrentLine.Length)
            {
                if (LineNumber < LineCount-1)
                {
                    LineDown();
                    RowPosition = 0;
                }
                else
                    RowPosition = CurrentLine.Length;
            }
            Console.CursorLeft = RowPosition;
        }

        public void MoveLeft()
        {
            RowPosition--;
            if (RowPosition < 0)
            {
                if (LineNumber > 0)
                {
                    LineUp();
                    RowPosition = CurrentLine.Length;
                }
                else
                    RowPosition = 0;
            }
            Console.CursorLeft = RowPosition;
        }

        public void InsertLine()
        {
            Buffer.Insert(LineNumber + 1, string.Empty);
            LineDown();
            RowPosition = 0;
            Redraw();
        }

        public void Redraw()
        {
            Console.Clear();
            Console.CursorLeft = 0;
            Console.CursorTop = 0;

            foreach (var line in Buffer)
                DrawLine(line);

            Console.CursorTop = LineNumber;
            Console.CursorLeft = RowPosition;
        }

        public void RedrawLine()
        {
            //Console.WriteLine();
            Console.CursorTop = LineNumber;
            Console.CursorLeft = 0;
            //for (int i = 0; i < Console.BufferWidth; i++)
            //    Console.Write(" ");
            Console.WriteLine();
            Console.CursorTop = LineNumber;
            Console.CursorLeft = 0;

            DrawLine(CurrentLine);
            Console.CursorLeft = RowPosition;
            Console.CursorTop = LineNumber;
        }

        public void WriteKey(char key)
        {
            var newLine = CurrentLine.Insert(RowPosition, key.ToString());
            CurrentLine = newLine;
            MoveRight();

            //Console.ForegroundColor = ConsoleColor.Black;
            //Console.WriteLine(CurrentLine);
            RedrawLine();
        }

        public void Delete()
        {
            if (RowPosition > 0)
            {
                var newLine = CurrentLine.Remove(RowPosition - 1, 1);
                CurrentLine = newLine;
                MoveLeft();
                RedrawLine();
            }
            else
            {
                DeleteLine();
            }
            
        }

        public void DeleteLine()
        {
            if (LineNumber > 0)
            {
                Buffer.RemoveAt(LineNumber);
                LineUp();
                RowPosition = CurrentLine.Length;
            }
            Redraw();
        }

        #endregion

        #region Helper Methods

        protected void DrawLine(string line)
        {
            //Console.ForegroundColor = ConsoleColor.Black;
            Console.CursorLeft = 0;
            //var tokens = Lexer.LexLine(line);
            //foreach (var t in tokens)
            //{
            //    SwitchMatchColor(t.Type);
            //    Console.Write(t.RawText);
            //}
            Console.WriteLine();
        }

        private static void SwitchMatchColor(TokenType token)
        {
            //switch (token)
            //{
            //    case TokenType.Comment:
            //        Console.ForegroundColor = COMMENT_COLOR;
            //        break;
            //    case TokenType.DomainBegin:
            //    case TokenType.DomainEnd:
            //        Console.ForegroundColor = DOMAIN_COLOR;
            //        break;
            //    case TokenType.FalseConstant:
            //    case TokenType.TrueConstant:
            //    case TokenType.VoidConstant:
            //    case TokenType.NumericConstant:
            //        Console.ForegroundColor = CONSTANT_COLOR;
            //        break;
            //    case TokenType.FunctionAssignment:
            //    case TokenType.FunctionCall:
            //        Console.ForegroundColor = FUNCTION_COLOR;
            //        break;
            //    case TokenType.GlobalAccess:
            //    case TokenType.ConstantAccess:
            //    case TokenType.PublicAccess:
            //    case TokenType.defKeyword:
            //    case TokenType.PrintKeyword:
            //    case TokenType.PrivateAccess:
            //    case TokenType.ReadOnlyAccess:
            //    case TokenType.ImportKeyword:
            //        Console.ForegroundColor = KEYWORD_COLOR;
            //        break;
            //    case TokenType.GroupingBegin:
            //    case TokenType.GroupingEnd:
            //    case TokenType.Operator:
            //        Console.ForegroundColor = OPERATOR_COLOR;
            //        break;
            //    case TokenType.StringConstant:
            //        Console.ForegroundColor = STRING_COLOR;
            //        break;
            //    case TokenType.VariableAssignment:
            //    case TokenType.VariableUsage:
            //        Console.ForegroundColor = VARIABLE_COLOR;
            //        break;
            //    case TokenType.Invalid:
            //        Console.ForegroundColor = ERROR_COLOR;
            //        break;
            //    default:
            //        Console.ForegroundColor = ConsoleColor.Black;
            //        break;
            //}
        }

        #endregion
    }
}
